<?php
/**
 * Redis连接池
*/

namespace houze\redis;

class Pool {
    /**
     * 配置项
    */
    protected $config;
    /**
     * 最少连接数
    */
    protected $min;
    /**
     * 最大连接数
    */
    protected $max;
    /**
     * 当前连接数
    */
    protected $count;
    /**
     * 连接池组
    */
    protected $connections;
    
    protected static $instance = null;
    private function __clone() {}

    /**
     * 初始化参数
    */
    private function __construct() {
        // 读取配置类
        $this->config = \houze\Config::get('redis.pool_list');
    }

    /**
     * 单例入口
    */
    public static function run() {
        # 只有第一次调用，才允许创建对象实例
        if (empty(self::$instance)) {
            self::$instance = new \houze\redis\Pool();
        }
        return self::$instance;
    }

    /**
     * 启动连接池
    */
    public function init() {
        $start_time = explode(' ',microtime());
        foreach ($this->config as $key=>$value) {
            if ($value['pool_num'] <= 0) return false;
            $this->min = ($value['pool_num']>1) ? ceil($value['pool_num']/2) : 1;
            $this->max = $value['pool_num'];
            
            for ($i=0; $i<$this->max; $i++) {
                $this->connections[] = $this->createRedis($value);
            }
        }
        
        \houze\StartRecord::mysql_reload($start_time);
        \houze\StartRecord::mysql_monitor();
        
        return $this;
    }

    /**
     * 获取一个连接
    */
    public function pop($key) {
        if (!isset($this->config[$key])) return false;
        
        // 重置连接
        $this->recovery($key);
        
        // 队列被取空
        if (!$this->connections) {
            $obj = $this->createRedis($this->config[$key]);
        } else {
            $obj = array_pop($this->connections);
        }
        return $obj['db'];
    }

    /**
     * 归还一个连接
    */
    public function free($db) {
        if ($db) {
            if(count($this->connections) < $this->max){
                $obj = [
                    'last_used_time' => time(),
                    'db' => $db,
                ];
            
                $this->connections[] = $obj;
            }else{
                unset($db);
            }
            
            // 统计连接数
            $this->count($this->connections);
            return true;
        }
        return false;
    }
    
    /**
     * 重置连接
    */
    protected function recovery($key) {
        $config = \houze\Config::get('redis');
        
        // 没用就回收重置连接时间
        $spare_time = $config['spare_time'];
        
        $list = [];
        // 阻塞循环
        while (true){
            if ($this->connections) {
                $obj = array_pop($this->connections);
                # 拿出最近一次使用时间
                $last_used_time = $obj['last_used_time'];
                # 判断回收重连超期时间
                if (time() - $last_used_time > $spare_time) {
                    array_push($list, $this->createRedis($this->config[$key]));  
                }else{
                    array_push($list, $obj);
                }
            }else{
                break;
            }
        }
        foreach ($list as $item) {
            $this->connections[] = $item;
        }
        // 统计连接数
        $this->count($list);
        
        unset($list);
    }
    
    /**
     * 统计重置连接数
    */ 
    protected function count($arr){
        $num = count($arr);
        $path = BOX_PATH.'env'.DS.'redis_pool_num.count';
        $json = file_get_contents($path);
        $array = [];
        if ($json) {
            $array = json_decode($json, true);
        }
        $array[0] = $num;
        file_put_contents($path, json_encode($array));
       
        unset($json);
        unset($array);
        unset($path);
    }

    /**
     * 创建数据库连接实例
    */
    protected function createRedis($database) {
        $redis = new \Redis();
        $redis->connect($database['host'], $database['port']);
        $redis->select($database['dbindex']);
        if(!empty($database['pwd'])) $redis->auth($database['pwd']);
        
        return [
            'last_used_time' => time(),
            'db' => $redis,
        ];
    }
}